#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/irq.h>
#include <linux/poll.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/arch/regs-gpio.h>

#define KEYS_NUMBER 4

//设备类
static struct class *keys_class = NULL;
//子设备类
static struct class_device *keys_class_devs[KEYS_NUMBER] = {NULL};
//主设备号
int major = 0;

//休眠
static DECLARE_WAIT_QUEUE_HEAD(key_waitq);
//中断置1 ，读取时置0
static volatile int ev_press;

static struct fasync_struct *key_async;

struct ping_desc
{
    unsigned int pin;
    unsigned int key_val;
};

/*
 * 四个按键按下时值分别为0x01,0x02,0x03,0x04
 * 四个按键送开时值分别为0x81,0x82,0x83,0x84
 */
static unsigned char key_val = 0;

struct ping_desc pings_desc[4] = {
    {S3C2410_GPF0, 0X01},
    {S3C2410_GPF2, 0X02},
    {S3C2410_GPG3, 0X03},
    {S3C2410_GPG11, 0X04},
};

/*
 * irq 中断号
 * dev_id 注册驱动程序时传入的
 */
static irqreturn_t key_irq(int irq, void *dev_id)
{
    struct ping_desc *pin = (struct ping_desc *)dev_id;
    unsigned int pinval = 0;
    pinval = s3c2410_gpio_getpin(pin->pin);
    if (pinval)
        key_val = 0x80 | pin->key_val;
    else
        key_val = pin->key_val;

    ev_press = 1;
    //唤醒休眠程序
    wake_up_interruptible(&key_waitq);

    //发送信号
    kill_fasync(&key_async, SIGIO, POLL_IN);

    //    printk("key val = %x", key_val);
    return IRQ_RETVAL(IRQ_HANDLED);
}

static int keys_open(struct inode *inode, struct file *file)
{
    int minor = MINOR(inode->i_rdev);

    // GPF0,2 GPG11,19
    // 注册中断函数
    // IRQ_EINT0 中断号
    // key_irq 处理函数
    // IRQT_BOTHEDGE 双边延触发
    // "key1" 驱动名称
    // 驱动ID，进入中断时会传入,我们传入相应的结构体指针地址
    request_irq(IRQ_EINT0, key_irq, IRQT_BOTHEDGE, "key1", &pings_desc[0]);
    request_irq(IRQ_EINT2, key_irq, IRQT_BOTHEDGE, "key2", &pings_desc[1]);
    request_irq(IRQ_EINT11, key_irq, IRQT_BOTHEDGE, "key3", &pings_desc[2]);
    request_irq(IRQ_EINT19, key_irq, IRQT_BOTHEDGE, "key4", &pings_desc[3]);

    return 0;
}

/*
 * buf 传入数据的缓存地址
 * count 传入数据的长度
 */
ssize_t keys_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{

    int minor = MINOR(file->f_dentry->d_inode->i_rdev);

    if (size < 1)
        return -EINVAL;

    // 等待按键按下
    wait_event_interruptible(key_waitq, ev_press);

    //将值传入用户空间
    copy_to_user(buf, &key_val, 1);

    ev_press = 0;

    //获取应用程序传入的值
    //copy_from_user(&val, buf, 4);

    return 1;
}

int keys_close(struct inode *inode, struct file *file)
{
    // 删除已经注册的中断
    free_irq(IRQ_EINT0, &pings_desc[0]);
    free_irq(IRQ_EINT2, &pings_desc[1]);
    free_irq(IRQ_EINT11, &pings_desc[2]);
    free_irq(IRQ_EINT19, &pings_desc[3]);

    return 0;
}

static unsigned keys_poll(struct file *file, poll_table *wait)
{
    unsigned int mask = 0;

    poll_wait(file, &key_waitq, wait); //不会立即进行休眠

    if (ev_press)
        mask |= POLLIN | POLLRDNORM;

    return mask;
}

/*
 * 初始化 key_async
 * 应用程序执行 fcnt1 时被调用
 */
static int keys_fasync(int fd, struct file *file, int on)
{
    return fasync_helper(fd, file, on, &key_async);
}

static struct file_operations keys_fops = {
    .owner = THIS_MODULE,
    .open = keys_open,
    .read = keys_read,
    .release = keys_close,
    .poll = keys_poll,
    .fasync = keys_fasync,
};

int keys_init(void)
{
    int i = 0;

    //注册驱动程序
    major = register_chrdev(0, "keys_dev", &keys_fops);

    //创建一个class
    keys_class = class_create(THIS_MODULE, "keys");
    if (IS_ERR(keys_class))
        return PTR_ERR(keys_class);

    //在类下面创建子设备
    for (i = 0; i < KEYS_NUMBER; i++)
    {
        keys_class_devs[i] = class_device_create(keys_class, NULL, MKDEV(major, i), NULL, "key%d", i);
        if (unlikely(IS_ERR(keys_class_devs[i])))
            return PTR_ERR(keys_class_devs[i]);
    }

    return 0;
}

void keys_exit(void)
{
    int i = 0;

    //移除注册驱动程序
    unregister_chrdev(major, "keys_dev");

    //移除驱动程序类下面的设备
    for (i = 0; i < KEYS_NUMBER; i++)
    {
        if (keys_class_devs[i] != NULL)
        {
            class_device_unregister(keys_class_devs[i]);
            keys_class_devs[i] = NULL;
        }
    }

    //移除驱动程序相关类
    class_destroy(keys_class);
}

module_init(keys_init); //添加入口函数
module_exit(keys_exit); //删除入口函数

MODULE_LICENSE("GPL");
